/** MTBLib.ShuffleStack by Medi H.B.T. */

namespace MTBLib {
    /**
     * @class MTBLib.ShuffleStack
     * @brief 用于抽号的顺序打乱列表
     * 注意，这是一个索引列表，也就是说，你不能给这个列表里的元素赋值。 */
    public class ShuffleStack: GLib.Object {
        ///< constants
        /** @brief 错误代码 */
        public enum ErrorType {
            NO_ERR   = 0x0,
            BASE     = 0x80020000, ///< ShuffleStack的基准错误码
            EMPTY    = 0x80020001, ///< 列表为空(没有未抽取的元素)
            FULL     = 0x80020002, ///< 列表已满
            OVERFLOW = 0x80020003, ///< 输入的值溢出
        }

        ///< fields
        public int []id_buff;       ///< id列表
        public int []rand_stack;    ///< 存放取得的随机下标的栈
        public int unshuffled_top;  ///< 未被抽取的栈顶
        public int rand_top;        ///< 随机下标栈的栈顶
        public GLib.Rand rand;      ///< 伪随机数生成器(PRNG)

        /** @fn ShuffleStack.ShuffleStack(int)
         * @brief 构造函数: 创建一个有固定长度的顺序打乱列表
         * @param elem_num 元素数量 */
        public ShuffleStack(int elem_num) {
            rand = new GLib.Rand();
            id_buff = new int[elem_num];
            rand_stack = new int[elem_num];
            unshuffled_top = elem_num;
            rand_top = 0;
            for (int i = 0; i < elem_num; i++)
                id_buff[i] = i;
        }

        ///< Methods
        /** 列表的基本属性 */
        public bool empty() { return (unshuffled_top == 0); }
        public bool full()  { return (unshuffled_top == id_buff.length);}
        public int  extracted_length() { return rand_top; }
        /** @fn extracted_at(int)
         * @brief 查找第index个被抽取的元素。
         * @param index 从0开始.
         * @return int 被抽取的元素元素id */
        public int  extracted_at(int index)
        {
            if (index < 0 || index >= rand_top)
                return ErrorType.OVERFLOW;
            return id_buff[id_buff.length - index - 1];
        }

        /** @fn draw()
         * @brief 抽取一个元素，与栈顶交换，然后弹出栈顶
         * @return int 被抽取的id. 若大于0x8000'0000,那么出错了. */
        public int  draw()
        {
            if (unshuffled_top == 0)
                return ErrorType.EMPTY;
            int target = (int)(rand.next_int() % unshuffled_top);

            /* 交换栈顶与被抽取的元素，然后弹出id栈顶 */
            int tmp = id_buff[unshuffled_top - 1];
            id_buff[unshuffled_top - 1] = id_buff[target];
            id_buff[target] = tmp;
            unshuffled_top--;
            /* 压随机下标栈 */
            rand_stack[rand_top] = target;
            rand_top++;
            return id_buff[unshuffled_top];
        }

        /** @fn draw_lazy()
         * @brief 随机抽取一个元素，然后什么都不做.
         * @return int 被抽取的id. 若大于0x8000'0000,那么出错了. */
        public int  draw_lazy()
        {
            if (unshuffled_top == 0)
                return ErrorType.EMPTY;
            return (int)(rand.next_int() % unshuffled_top);
        }

        /** @fn undo()
         * @brief 撤销上一步draw()操作
         * @return ErrorType 错误码, 0表示没有错误. */
        public ErrorType undo()
        {
            if (unshuffled_top == id_buff.length)
                return ErrorType.FULL;
            /* 弹随机下标栈 */
            rand_top--;
            int recent_index = rand_stack[rand_top];
            /* 交换上一个栈顶与上一个随机下标, 然后回复id栈顶 */
            int tmp = id_buff[unshuffled_top];
            id_buff[unshuffled_top] = id_buff[recent_index];
            id_buff[recent_index] = tmp;
            unshuffled_top++;
            return ErrorType.NO_ERR;
        }

        /** @fn reset()
         * @brief   重置整个列表，包括历史记录
         * @warning 注意，该函数不会重置元素原有的排序状态。你可以看成，
         *   执行一次该函数后，列表变成“脏的”。*/
        public void reset()
        {
            unshuffled_top = id_buff.length;
            rand_top = 0;
        }
    }
}
